using System;
using System.Collections;
using System.Text;

namespace csammisrun.OscarLib.Utility
{
	/// <summary>
	/// Provides static methods for processing messages in SNAC family 0x000E -- chat service
	/// </summary>
	internal static class SNAC0E
	{
    /// <summary>
    /// Processes room information -- SNAC(0E,02)
    /// </summary>
    /// <param name="dp"></param>
    public static void ProcessRoomInfoUpdate(DataPacket dp)
    {
      int index = 0;

      ushort exchange = Marshal.ByteArrayToUshort(dp.Data, ref index);
      byte strlen = dp.Data[index++];
      string roomname = Marshal.ByteArrayToString(dp.Data, ref index, strlen, Marshal.ASCII);
      ushort detaillevel = Marshal.ByteArrayToUshort(dp.Data, ref index);
      ushort tlvcount = Marshal.ByteArrayToUshort(dp.Data, ref index);

      string roomname2 = "";
      ushort usercount = 0x0000;
      ArrayList users = new ArrayList();
      ushort flags = 0x0000;
      uint creationtime = 0x00000000;
      ushort maxmessagelength = 0x0000;
      ushort maxvisiblemessagelength = 0x0000;
      string description = "";
      
      while (index < dp.Data.Length)
      {
        ushort key = Marshal.ByteArrayToUshort(dp.Data, ref index);
        ushort keylength = Marshal.ByteArrayToUshort(dp.Data, ref index);
        switch (key)
        {
          case 0x006A:
            roomname2 = Marshal.ByteArrayToString(dp.Data, ref index, keylength, Marshal.ASCII);
            break;
          case 0x006F:
            usercount = Marshal.ByteArrayToUshort(dp.Data, ref index);
            break;
          case 0x0073:
            int stopindex = index + keylength;
            while (index < stopindex)
            {
              users.Add(Marshal.GetUserinfoBlock(dp.Data, ref index));
            }
            break;
          case 0x00C9:
            flags = Marshal.ByteArrayToUshort(dp.Data, ref index);
            break;
          case 0x00CA:
            creationtime = Marshal.ByteArrayToUint(dp.Data, ref index);
            break;
          case 0x00D1:
            maxmessagelength = Marshal.ByteArrayToUshort(dp.Data, ref index);
            break;
          case 0x00D3:
            description = Marshal.ByteArrayToString(dp.Data, ref index, keylength, Marshal.ASCII);
            break;
          case 0x00DA:
            maxvisiblemessagelength = Marshal.ByteArrayToUshort(dp.Data, ref index);
            break;
          default:
            index += keylength;
            break;
        }
      }
    }

    /// <summary>
		/// Processes a list of users who have joined a chat room -- SNAC(0E,03)
		/// </summary>
		/// <param name="dp">A <see cref="DataPacket"/> object containing SNAC(0E,03)</param>
		public static void ProcessUsersJoined(DataPacket dp)
		{
			byte[] SNACData = dp.Data;

			int index = 0;
			bool valid = false;
			while(index < SNACData.Length)
			{
				UserInfo ui = Marshal.GetUserinfoBlock(SNACData, ref index, ref valid);
				if(valid)
				{
          ChatConnection conn = (ChatConnection)dp.ParentConnection;
          ChatRoomInfo room = dp.ParentSession.Connections.GetChatByConnection(conn);
					dp.ParentSession.OnChatRoomUserJoined(room, ui);
				}
			}
		}

		/// <summary>
		/// Processes a list of users who have left a chat room -- SNAC(0E,04)
		/// </summary>
		/// <param name="dp">A <see cref="DataPacket"/> object containing SNAC(0E,04)</param>
		public static void ProcessUsersLeft(DataPacket dp)
		{
			byte[] SNACData = dp.Data;

			int index = 0;
			bool valid = false;
			while(index < SNACData.Length)
			{
				UserInfo ui = Marshal.GetUserinfoBlock(SNACData, ref index, ref valid);
				if(valid)
				{
          ChatConnection conn = (ChatConnection)dp.ParentConnection;
          ChatRoomInfo room = dp.ParentSession.Connections.GetChatByConnection(conn);
					dp.ParentSession.OnChatRoomUserLeft(room, ui);
				}
			}
		}

    /// <summary>
    /// Sends a message to a chat room -- SNAC(0E,05)
    /// </summary>
    /// <param name="sess">A <see cref="Session"/> object</param>
    /// <param name="roominfo">The <see cref="ChatRoomInfo"/> describing the room</param>
    /// <param name="message">The message to send</param>
    /// <param name="encoding">The text encoding of the message</param>
    /// <param name="language">The 2-character language of the message</param>
    /// <param name="flags">A <see cref="MessageFlags"/> enumeration, see Remarks</param>
    /// <remarks><paramref name="flags"/> has a slightly different meaning than the flags argument used in
    /// one-to-one ICBMs.  Here, two flags are valid: <see cref="MessageFlags.AutoResponse"/>, to indicate
    /// that the message being sent is an away message, and <see cref="MessageFlags.RequestAcknowledgement"/>
    /// to request that the server relay our own message back to us.</remarks>
    public static void SendChatRoomMessage(Session sess, ChatRoomInfo roominfo,
      string message, Encoding encoding, string language, MessageFlags flags)
    {
      SNACHeader sh = new SNACHeader();
      sh.FamilyServiceID = (ushort)SNACFamily.ChatService;
      sh.FamilySubtypeID = (ushort)ChatService.MessageFromClient;
      sh.Flags = 0x0000;
      sh.RequestID = sess.GetNextRequestID();

      int innerindex = 0;
      bool havelanguage = !String.IsNullOrEmpty(language);

      TLV[] inner = new TLV[2 + (havelanguage ? 1 : 0)];
      inner[innerindex++] = TLVMarshal.MakeTLV(0x0001, Marshal.StringToByteArray(message, Marshal.ASCII));
      inner[innerindex++] = TLVMarshal.MakeTLV(0x0002, Marshal.StringToByteArray(encoding.WebName, Marshal.ASCII));
      if(havelanguage)
        inner[innerindex++] = TLVMarshal.MakeTLV(0x0003, Marshal.StringToByteArray(language, Marshal.ASCII));

      int outerindex = 0;
      TLV[] outer = new TLV[2 + ((flags & MessageFlags.AutoResponse) == 0 ? 0 : 1) +
        ((flags & MessageFlags.RequestAcknowledgement) == 0 ? 0 : 1)];
      outer[outerindex++] = TLVMarshal.MakeTLV(0x0001, null);
      if ((flags & MessageFlags.RequestAcknowledgement) != 0)
        outer[outerindex++] = TLVMarshal.MakeTLV(0x0006, null);
      if ((flags & MessageFlags.AutoResponse) != 0)
        outer[outerindex++] = TLVMarshal.MakeTLV(0x0007, null);

      outer[outerindex++] = TLVMarshal.MakeTLV(0x0005, TLVMarshal.TLVsToByteArray(inner));

      int tlvsize = 0;
      foreach (TLV tlv in outer)
      {
        tlvsize += 4 + tlv.DataSize;
      }

      int index = 0;
      byte[] buffer = new byte[8 + 2 + tlvsize];

      byte[] cookie = new byte[8];
      (new Random()).NextBytes(cookie);

      Marshal.CopyArray(cookie, buffer, 0, ref index);
      Marshal.InsertUshort(buffer, 0x0003, ref index);
      Marshal.InsertTLVs(outer, buffer, ref index);

      DataPacket dp = Marshal.BuildDataPacket(sess, sh, buffer);
      dp.ParentConnection = roominfo.Connection;
      SNACFunctions.BuildFLAP(dp);
    }

    /// <summary>
    /// Processes a message received from a chat room -- SNAC(0E,06)
    /// </summary>
    /// <param name="dp">A <see cref="DataPacket"/> object containing SNAC(0E,06)</param>
    public static void ProcessChatRoomMessage(DataPacket dp)
    {
      UserInfo sender = new UserInfo();
      byte[] message = null;
      Encoding encoding = Marshal.ASCII;
      string language = "";

      int index = 10; // Skip the cookie and the channel
      while (index < dp.Data.Length)
      {
        ushort key = Marshal.ByteArrayToUshort(dp.Data, ref index);
        ushort keylength = Marshal.ByteArrayToUshort(dp.Data, ref index);
        switch (key)
        {
          case 0x0003:
            sender = Marshal.GetUserinfoBlock(dp.Data, ref index);
            break;
          case 0x0005:
            int stopindex = index + keylength;
            while (index < stopindex)
            {
              ushort innerkey = Marshal.ByteArrayToUshort(dp.Data, ref index);
              ushort innerlen = Marshal.ByteArrayToUshort(dp.Data, ref index);
              switch (innerkey)
              {
                case 0x0001:
                  message = new byte[innerlen];
                  Marshal.CopyArray(dp.Data, message, ref index);
                  break;
                case 0x0002:
                  encoding = Marshal.AolMimeToEncoding(Marshal.ByteArrayToString(dp.Data, ref index, innerlen, Marshal.ASCII));
                  break;
                case 0x0003:
                  language = Marshal.ByteArrayToString(dp.Data, ref index, innerlen, Marshal.ASCII);
                  break;
                default:
                  index += innerlen;
                  break;
              }
            }
            break;
          default:
            index += keylength;
            break;
        }
      }

      string msg = encoding.GetString(message, 0, message.Length);

      ChatConnection conn = (ChatConnection)dp.ParentConnection;
      ChatRoomInfo room = dp.ParentSession.Connections.GetChatByConnection(conn);
      dp.ParentSession.OnChatRoomMessageReceived(room, sender, msg, encoding, language);
    }
	}
}
